#include "trivia.h"

//Contructores
Trivia::Trivia(){
    Lista<Keyword> keywords;
    Lista<Pregunta> preguntas;
    Pregunta pregunta;
    preguntas.agregar(pregunta);
    init(keywords, preguntas);
}

Trivia::Trivia(const Lista<Keyword>& ks, const Lista<Pregunta>& ps){
   init (ks, ps);
}

void Trivia::init (const Lista<Keyword>& ks, const Lista<Pregunta>& ps ){
    _keywords = ks;
    _preguntas = ps;

    _participantes = Lista<Usuario>();
    _ganadores = Lista<Usuario>();

    _preguntasPorUsuario = Lista< pair<Usuario, int> >();
    _puntajeUsuario = Lista< pair<Usuario, int> >();
}

Trivia::Trivia(const Trivia& t){
    _keywords = t._keywords;
    _preguntas = t._preguntas;
    _participantes = t._participantes;
    _ganadores = t._ganadores;
    _preguntasPorUsuario = t._preguntasPorUsuario;
    _puntajeUsuario = t._puntajeUsuario;
}

//Getters
Lista<Keyword> Trivia::keywords() const{
    return _keywords;
}

Lista<Pregunta> Trivia::preguntas() const{
    return _preguntas;
}

Lista<Usuario> Trivia::participantes() const{
    return _participantes;
}

Lista<Usuario> Trivia::ganadores() const{
    return _ganadores;
}

//proxPregunta
Pregunta Trivia::proxPregunta(Usuario u) const{
    int indice = devolverSegundo(_preguntasPorUsuario, u);
    return preguntas().iesimo(indice);
}

//puntajeAcmumulado
Puntaje Trivia::puntajeAcumulado(Usuario u) const{
    return devolverSegundo(_puntajeUsuario, u);
}

int Trivia::devolverSegundo(const Lista< pair<Usuario,
            int> >& lista, const Usuario& u) const{

    int i = 0;
    int res = -1;

    while( i < lista.longitud() ){
        if( lista.iesimo(i).first == u )
            res = lista.iesimo(i).second;
        i++;
    }

    if(res != -1)
        return res;
    else
        cerr << "Usuario no existente.{puntajeAcumulado}";

}
//Fin proxPregunta y puntajeAcumulado

//registrarJugador
SMS Trivia::registrarJugador(Usuario u, Keyword k){
    SMS res;

    _participantes.agregarAtras(u);
    _puntajeUsuario.agregarAtras( pair <Usuario,int> (u,0) );
    _preguntasPorUsuario.agregarAtras ( pair <Usuario,int> (u,0));

    res = SMS(u,"Hola, tenes 0 puntos. " + preguntas().cabeza().texto());

    return res;
}

//desregistrarJugador
void Trivia::desregistrarJugador(Usuario u){
    int indiceProxPregunta = preguntas().posicion(proxPregunta(u));
    pair <Usuario,int> parPuntajeUsuario(u,puntajeAcumulado(u));
    pair <Usuario,int> parProximaPregunta(u,indiceProxPregunta);
    _participantes.sacar(u);
    _puntajeUsuario.sacar(parPuntajeUsuario);
    _preguntasPorUsuario.sacar(parProximaPregunta);
    _ganadores.sacar(u);
}

//procesarRespuesta
SMS Trivia::procesarRespuesta(Usuario u, Texto r){

    SMS res;
    int indicePreguntaActual;
    Pregunta preguntaActual;
    int indicePuntajeUsuario;
    int puntajeUsuario;
    string rtaCorrecta;

    //Si ya es ganador no hago nada
    if( ganadores().pertenece(u) ){
        //Gracias por seguir pagando
        res = SMS(u, "Gracias por seguir pagando pero el juego termino.");
    //Si no es ganador
    }else{
        indicePuntajeUsuario = devolverSegundo(_puntajeUsuario, u);
        puntajeUsuario = puntajeAcumulado(u);
        indicePreguntaActual = devolverSegundo(_preguntasPorUsuario, u);
        preguntaActual = proxPregunta(u);
        rtaCorrecta = preguntaActual.rtaCorrecta();
        //Limpio la respuesta del usuario
        r = sacarPrimerPalabra(r);
        //Si la pregunta es correcta
        if(palabras(r) == palabras (rtaCorrecta)) {
            //Sumo puntaje
            _puntajeUsuario.sacar(pair <Usuario,int> ( u, puntajeUsuario ));
            _puntajeUsuario.agregar(pair <Usuario,int> ( u ,
                        puntajeUsuario + preguntaActual.puntaje() ));
        }

        //Remuevo la referencia a la proxima pregunta, luego veo que hago.
        _preguntasPorUsuario.sacar(pair <Usuario, int> ( u, indicePreguntaActual ));

        //Si era la ultima
        if( indicePreguntaActual == preguntas().longitud() - 1 ){
            //Mensaje ultima
            _ganadores.agregar(u);
            res = SMS(u, "Has respondido todas las preguntas. "+textoPuntaje(u) );
        //Sino era la ultima
        }else{

            //Agrego la referencia a la proxima pregunta pero con un + 1
            _preguntasPorUsuario.agregar(
                pair <Usuario, int> ( u, indicePreguntaActual + 1 ));

            //Devuelvo el mensaje segun corresponda
            if(palabras(r) == palabras(rtaCorrecta))
               res = SMS(u,"Bien! "+textoPuntaje(u)+" "+proxPregunta(u).texto());
            else
               res = SMS(u,"Mal! "+textoPuntaje(u)+" "+proxPregunta(u).texto());
        }
    }

    return res;
}

string Trivia::textoPuntaje(const Usuario& u) const{
    return "Tenes "+ intToString( puntajeAcumulado(u) )+ " puntos.";
}
//Fin procesarRespuesta

//usuariosEficientes
Lista<Usuario> Trivia::usuariosEficientes() const{
    return losDeMasPuntajeYMenosMensajes(
                losDeMasPuntaje(maximoPuntaje()), menorCantDeMensajes());
}

Lista<Usuario> Trivia:: losDeMasPuntajeYMenosMensajes(
            const Lista<Usuario>& listaDeUsuarios, int min) const{
    Lista<Usuario> res;
    int i=0;
    while (i<listaDeUsuarios.longitud()){
        if (_preguntasPorUsuario.iesimo(i).second == min)
            res.agregar(listaDeUsuarios.iesimo(i));
        i++;
    }
return res;
}

int Trivia:: menorCantDeMensajes() const{
    int i=0;
    int res = _preguntasPorUsuario.iesimo(i).second;
    while (i<_preguntasPorUsuario.longitud()){
        if(res < _preguntasPorUsuario.iesimo(i).second)
            res = _preguntasPorUsuario.iesimo(i).second;
        i++;
    }
    return res;
}

Lista<Usuario> Trivia:: losDeMasPuntaje(int max) const{
    Lista<Usuario> usuarios;
    int i=0;
    while (i<_puntajeUsuario.longitud()){
        if (_puntajeUsuario.iesimo(i).second == max)
            usuarios.agregar(_puntajeUsuario.iesimo(i).first);
        i++;
    }
    return usuarios;
}

int Trivia:: maximoPuntaje() const{
    int i=0;
    int res = _puntajeUsuario.iesimo(i).second;
    while (i<_puntajeUsuario.longitud()){
        if(res < _puntajeUsuario.iesimo(i).second)
            res = _puntajeUsuario.iesimo(i).second;
        i++;
    }
    return res;
}
//Fin usuariosEficientes

//posicion
int Trivia::posicion(Usuario u) const{
    int res = 0;
    if (!participantes().pertenece(u))
        cerr << "Rompe el requiere (u no pertenece a la trivia){posicionEnT}";
    if (ganadores().pertenece(u))
        res = ganadores().posicion(u);
    else
        res = ganadores().longitud() + tienenMasPuntos(u);
    return res;
}

int Trivia::tienenMasPuntos(const Usuario& u) const{
    int res = 0;
    int i = 0;
    Usuario v;
    while (i < participantes().longitud()){
        v = participantes().iesimo(i);
        if (puntajeAcumulado(u) < puntajeAcumulado(v)
            && !(ganadores().pertenece(v))){
            res++;
            }
        i++;
    }
    return res;
}
//Fin posicion

//palabrasRecurrentes
Lista<string> Trivia::palabrasRecurrentes(int n) const{
    Lista<string> res;
    int i, ap = 0, apFinal = 0;
    Lista<string> filtradas = palabrasMayoresA(n);
    cout << filtradas << endl;
    while (filtradas.longitud() != 0){
        //Cuenta apariciones
        i = 0;
        ap = 0;
        while (i < filtradas.longitud()){
            if (filtradas.iesimo(0) == filtradas.iesimo(i))
                ap++;
            i++;
        } //Fin cuenta apariciones
        if (ap > apFinal){
            apFinal = ap;
            res = Lista<string>();
        }
        if (ap == apFinal)
            res.agregar(filtradas.iesimo(0));
        filtradas.sacar(filtradas.iesimo(0));
    }
    return res;
}

Lista<string> Trivia::palabrasMayoresA (int n) const{
    Lista<string> res;
    Lista<Pregunta> ps = preguntas();
    int i = 0;
    int j;
    while (i < ps.longitud()){
        Lista<string> pal = palabras(ps.iesimo(i).texto());
        pal.concatenar(palabras(ps.iesimo(i).rtaCorrecta()));
        j = 0;
        while (j < pal.longitud()){
            if (pal.iesimo(j).length() >= n)
                res.agregar(pal.iesimo(j));
            j++;
        }
        i++;
    }
    return res;
}
//Fin palabrasRecurrentes


//Operadores
bool Trivia::operator==(const Trivia& t) const{
    return keywords() == t.keywords() &&
           preguntas() == t.preguntas() &&
           participantes() == t.participantes() &&
           ganadores() == t.ganadores() &&
           _preguntasPorUsuario == t._preguntasPorUsuario &&
           _puntajeUsuario == t._puntajeUsuario;
}

//mostrar
void Trivia::mostrar(std::ostream& os) const{
    os << "Keywords: " << keywords() << endl;
    os << "Preguntas: "<< endl;
    mostrarPreguntas (os) ;
    os << "Participantes : " << participantes() << endl;
    os << "Ganadores : " << ganadores() << endl;
    os << "Puntajes por usuario: " << _puntajeUsuario << endl;
    os << "Preguntas por usuario : " << _preguntasPorUsuario << endl;
}

void Trivia :: mostrarPreguntas (std::ostream& os) const{
    int i=0;
    while (i < preguntas().longitud()){
        preguntas().iesimo(i).mostrar(os);
        i++;
    }

}
//Fin mostrar

//guardar
void Trivia::guardar(std::ostream& os) const{

    char c = ENCABEZADO_ARCHIVO;
    os << c << " [" ;
    guardarKeywords(os);
    os<< "] " << "[";
    guardarPreguntas(os);
    os<< "] " << "[";
    triplaUsuario(os);
    os << "]";

}

void Trivia::triplaUsuario(std::ostream& os) const{
    int i=0;
    while (i < participantes().longitud()){
        if(ganadores().pertenece(participantes().iesimo(i)))
            triplaSiSosGanador(os,i);
        else
            triplaSiNoSosGanador(os,i);
        i++;
    }
}

void Trivia:: triplaSiSosGanador(std::ostream& os, int i) const{
    if(i != 0) os << ",";
    os<< "(" << participantes().iesimo(i) << ","
    << devolverSegundo(_puntajeUsuario, participantes().iesimo(i))
    << "," << preguntas().longitud() << ")";

}


void Trivia:: triplaSiNoSosGanador(std::ostream& os, int i) const{
    if(i != 0) os << ",";
    os<< "(" << participantes().iesimo(i) << ","
    << devolverSegundo(_puntajeUsuario, participantes().iesimo(i))
    << "," << devolverSegundo(_preguntasPorUsuario, participantes().iesimo(i)) << ")";

}

void Trivia::guardarKeywords(std::ostream& os) const{
    int i=0;
    while (i < keywords().longitud()){
        if(i != 0) os << ",";
        os<< "|" << keywords().iesimo(i) << "|";
        i++;
    }
}

void Trivia::guardarPreguntas(std::ostream& os) const {
    int i=0;
    while (i < preguntas().longitud()){
        if(i != 0) os << ", ";
        preguntas().iesimo(i).guardar(os);
        i++;
    }

}
//Fin guardar

//cargar
void Trivia::cargar (std::istream& is){
    Lista<Keyword> keywords;
    Lista<Pregunta> preguntas;
    Lista<Usuario> usuarios = Lista<Usuario>();
    Lista< pair<Usuario,int> > puntajesUsuarios;
    Lista< pair<Usuario,int> > preguntasUsuarios;
    Lista<Usuario> ganadores;

    char c;
    Texto keyword;
    Pregunta pregunta;
    Usuario usuario;
    int puntajeAcumulado;
    int indicePregunta;

    //Si el encabezado coincide
    is >> c;
    if (c == ENCABEZADO_ARCHIVO){

        //Chupo keywords
        is >> c;
        while(c != ']')
        {
            is >> c;
            getline(is,keyword,'|');
            keywords.agregarAtras(keyword);
            is >> c;
        }

        //Chupo preguntas
        is >> c;
        while(c != ']')
        {
            pregunta.cargar(is);
            preguntas.agregarAtras(pregunta);
            is >> c;
        }

        //Chupo ternas -> Info de usuario
        is >> c;
        is >> c; //fix
        while(c != ']')
        {
            //Numero de usuario
            if( c != '(' ) is >> c; //fix
            is >> usuario;
            usuarios.agregarAtras(usuario);

            //Puntaje acumulado
            is >> c;
            is >> puntajeAcumulado;
            puntajesUsuarios.agregarAtras(pair <Usuario,int> (usuario,puntajeAcumulado));

            //Indice proxima pregunta
            is >> c;
            is >> indicePregunta;
            if (indicePregunta == preguntas.longitud())
                ganadores.agregarAtras(usuario);
            else
                preguntasUsuarios.agregarAtras(
                   pair <Usuario,int> (usuario,indicePregunta));

            //Saco caracteres finales
            is >> c;
            is >> c;
        }

        //Asigno resultados
        _keywords = keywords;
        _participantes = usuarios;
        _preguntas = preguntas;
        _preguntasPorUsuario = preguntasUsuarios;
        _puntajeUsuario = puntajesUsuarios;
        _ganadores = ganadores;
    }
//Fin cargar
}
